﻿using System;
using System.Collections;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;

namespace GenUI.WPF.GenControls
{
    public class PropertyGrid : Control
    {
        GroupBox GB;
        StackPanel SP;
        public double NameWidth
        {
            get { return (double)GetValue(NameWidthProperty); }
            set { SetValue(NameWidthProperty, value); }
        }

        // Using a DependencyProperty as the backing store for NameWidth.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty NameWidthProperty =
            DependencyProperty.Register("NameWidth", typeof(double), typeof(PropertyGrid), new PropertyMetadata(100d, OnNameWidthChanged));

        private static void OnNameWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is PropertyGrid PG && PG != null)
            {
                PG.Refresh(PG, PG.SelectObject);
            }
        }

        public object SelectObject
        {
            get { return GetValue(SelectObjectProperty); }
            set { SetValue(SelectObjectProperty, value); }
        }

        public static readonly DependencyProperty SelectObjectProperty =
            DependencyProperty.Register("SelectObject", typeof(object), typeof(PropertyGrid), new PropertyMetadata(null, OnSelectObjChanged));
        static PropertyGrid()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(PropertyGrid), new FrameworkPropertyMetadata(typeof(PropertyGrid)));
        }
        private static void OnSelectObjChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is PropertyGrid PropGrid && e.NewValue != null)
            {
                PropGrid.Refresh(PropGrid, e.NewValue);
            }
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            this.GB = GetTemplateChild("PART_GB") as GroupBox;
            this.SP = GetTemplateChild("PART_SP") as StackPanel;
            Refresh(this, this.SelectObject);
        }
        private void Refresh(PropertyGrid PropGrid, object obj)
        {
            if (PropGrid != null && obj != null && GB != null)
            {
                GB.Header = obj.GetType().GetPropGridMapName();
                SP.Children.Clear();
                var props = obj.GetType().GetProperties();

                foreach (var prop in props)
                {
                    bool isArr = typeof(IEnumerable).IsAssignableFrom(prop.PropertyType);
                    if (!isArr && prop.PropertyType.IsClass && prop.PropertyType.FullName != "System.String")
                    {
                        PropertyGrid propertyGrid = new PropertyGrid();
                        propertyGrid.NameWidth = PropGrid.NameWidth;
                        propertyGrid.SelectObject = prop.GetValue(obj);
                        SP.Children.Add(propertyGrid);
                    }
                    else
                    {
                        PropGrid.AddChild(obj, prop);
                    }
                }
            }
        }

        private void AddChild(object obj, PropertyInfo PropInfo)
        {
            if (PropInfo.PropertyType.IsEnum)
            {
                this.AddENUM(obj, PropInfo);
                return;
            }
            if (typeof(IEnumerable).IsAssignableFrom(PropInfo.PropertyType))
            {
                this.AddArray(obj, PropInfo);
                return;
            }
            switch (PropInfo.PropertyType.Name)
            {
                case "DateTime":
                    this.AddDateTime(obj, PropInfo);
                    break;
                case "Boolean":
                    this.AddBool(obj, PropInfo);
                    break;
                default:
                    this.AddStr(obj, PropInfo);
                    break;
            }
        }

        private void AddArray(object obj, PropertyInfo propInfo)
        {
            IEnumerable objects = propInfo.GetValue(obj) as IEnumerable;
            foreach (var item in objects)
            {
                SP.Children.Add(new TextBox() { Text = item.ToString(),Height=40d });
            }
        }

        private void AddENUM(object obj, PropertyInfo propInfo)
        {
            ObjectDataProvider objectDataProvider = new ObjectDataProvider()
            {
                MethodName = "GetValues",
                ObjectType = propInfo.PropertyType,
                MethodParameters = { propInfo.PropertyType }
            };
            Binding binding_ENUM = new Binding();
            binding_ENUM.Source = objectDataProvider.Data;
            binding_ENUM.Mode = BindingMode.Default;

            Binding binding_Values = new Binding();
            binding_Values.Source = obj;
            binding_Values.Mode = BindingMode.Default;
            binding_Values.Path = new PropertyPath(propInfo.Name);

            ComboBox comboBox = new ComboBox();
            comboBox.IsReadOnly = true;
            comboBox.SetValue(TextBoxHelper.TitleProperty, propInfo.GetPropGridMapName());
            comboBox.SetValue(TextBoxHelper.TitleWidthProperty, NameWidth);
            comboBox.SetValue(StyleProperty, Application.Current.FindResource("DefaultComboBoxStyle"));

            bool ReadOnly = propInfo.SetMethod.IsPrivate;
            bool WriteOnly = propInfo.GetMethod.IsPrivate;
            if (ReadOnly && !WriteOnly)//只读
            {
                binding_ENUM.Mode = BindingMode.OneWay;
                binding_Values.Mode = BindingMode.OneWay;
                comboBox.IsEnabled = false;
            }
            else if (!ReadOnly && WriteOnly)//只写
            {
                binding_Values.Mode = BindingMode.OneWayToSource;
                binding_ENUM.Mode = BindingMode.OneWayToSource;
            }

            if (ReadOnly && WriteOnly)//privte
            {
                comboBox.IsEnabled = false;
            }
            else//public
            {
                comboBox.SetBinding(ItemsControl.ItemsSourceProperty, binding_ENUM);
                comboBox.SetBinding(ComboBox.SelectedItemProperty, binding_Values);
            }
            if (!ReadOnly && WriteOnly)//只写
            {
                comboBox.ItemsSource = ((Enum)propInfo.GetValue(obj)).GetType().GetEnumValues();
                comboBox.SelectedValue = propInfo.GetValue(obj);
            }

            _ = SP.Children.Add(comboBox);
        }
        private void AddDateTime(object obj, PropertyInfo propInfo)
        {
            Binding binding_DateTime = new Binding();
            binding_DateTime.Source = obj;
            binding_DateTime.Mode = BindingMode.TwoWay;
            binding_DateTime.Path = new PropertyPath(propInfo.Name);

            DateTimePicker dateTimePicker = new DateTimePicker();
            dateTimePicker.SetValue(TextBoxHelper.TitleProperty, propInfo.GetPropGridMapName());
            dateTimePicker.SetValue(TextBoxHelper.TitleWidthProperty, NameWidth);

            bool ReadOnly = propInfo.SetMethod.IsPrivate;
            bool WriteOnly = propInfo.GetMethod.IsPrivate;
            if (ReadOnly && !WriteOnly)//只读
            {
                binding_DateTime.Mode = BindingMode.OneWay;
                dateTimePicker.IsEnabled = false;
            }
            else if (!ReadOnly && WriteOnly)//只写
            {
                binding_DateTime.Mode = BindingMode.OneWayToSource;
            }
            if (ReadOnly && WriteOnly)//privte
            {
                dateTimePicker.IsEnabled = false;
            }
            else//public
            {
                dateTimePicker.SetBinding(DateTimePicker.DateTimeStrProperty, binding_DateTime);
            }

            if (!ReadOnly && WriteOnly)//只写
            {
                dateTimePicker.DateTimeStr = DateTime.Parse(propInfo.GetValue(obj).ToString());
            }

            _ = SP.Children.Add(dateTimePicker);
        }
        private void AddStr(object obj, PropertyInfo propInfo)
        {
            Binding binding_IsChecked = new Binding();
            binding_IsChecked.Source = obj;
            binding_IsChecked.Mode = BindingMode.Default;
            binding_IsChecked.Path = new PropertyPath(propInfo.Name);

            TextBox textBox = new TextBox();
            textBox.SetValue(StyleProperty, Application.Current.FindResource("TitleBoxStyle"));
            textBox.SetValue(TextBoxHelper.TitleProperty, propInfo.GetPropGridMapName());
            textBox.SetValue(TextBoxHelper.TitleWidthProperty, NameWidth);

            bool ReadOnly = propInfo.SetMethod.IsPrivate;
            bool WriteOnly = propInfo.GetMethod.IsPrivate;
            if (ReadOnly && !WriteOnly)//只读
            {
                binding_IsChecked.Mode = BindingMode.OneWay;
                textBox.IsReadOnly = true;
            }
            else if (!ReadOnly && WriteOnly)//只写
            {
                textBox.SetValue(TextBox.TextProperty, propInfo.GetValue(obj).ToString());
                binding_IsChecked.Mode = BindingMode.OneWayToSource;
            }

            if (ReadOnly && WriteOnly)//privte
            {
                textBox.Text = propInfo.GetValue(obj).ToString();
            }
            else//public
            {
                textBox.SetBinding(TextBox.TextProperty, binding_IsChecked);
            }

            if (!ReadOnly && WriteOnly)//只写
            {
                textBox.SetValue(TextBox.TextProperty, propInfo.GetValue(obj).ToString());
            }

            _ = SP.Children.Add(textBox);
        }
        private void AddBool(object obj, PropertyInfo propInfo)
        {
            bool.TryParse(propInfo.GetValue(obj).ToString(), out bool boolValue);

            Binding binding_IsChecked = new Binding();
            binding_IsChecked.Source = obj;
            binding_IsChecked.Mode = BindingMode.TwoWay;
            binding_IsChecked.Path = new PropertyPath(propInfo.Name);

            CheckBox checkBox = new CheckBox();
            checkBox.HorizontalContentAlignment = HorizontalAlignment.Center;
            checkBox.VerticalContentAlignment = VerticalAlignment.Center;
            //checkBox.SetValue(StyleProperty, Application.Current.FindResource("SwitchButtonStyle"));

            Label label = new Label();
            label.SetValue(StyleProperty, Application.Current.FindResource("DefaultLableStyle"));
            label.FontSize = 18;
            label.Background = Brushes.Transparent;
            label.Content = propInfo.GetPropGridMapName();

            HeaderControl headerControl = new HeaderControl();
            headerControl.Margin = new Thickness(1);
            headerControl.Header = label;
            headerControl.HeaderWidth = NameWidth;

            bool ReadOnly = propInfo.SetMethod.IsPrivate;
            bool WriteOnly = propInfo.GetMethod.IsPrivate;
            if (ReadOnly && !WriteOnly)//只读
            {
                binding_IsChecked.Mode = BindingMode.OneWay;
                checkBox.IsEnabled = false;
            }
            else if (!ReadOnly && WriteOnly)//只写
            {
                binding_IsChecked.Mode = BindingMode.OneWayToSource;
            }
            if (ReadOnly && WriteOnly)//privte
            {
                checkBox.IsChecked = boolValue;
            }
            else//public
            {
                checkBox.SetBinding(CheckBox.IsCheckedProperty, binding_IsChecked);
            }

            if (!ReadOnly && WriteOnly)//只写
            {
                checkBox.IsChecked = boolValue;
            }

            headerControl.Content = checkBox;
            SP.Children.Add(headerControl);
        }
    }
}
